#include "keyword_dialog.h"
#include "app_frame.h"
#include "util.h"
#include "unicode.h"
#include "name_dialog.h"

//(*InternalHeaders(KeywordDialog)
#include <wx/intl.h>
#include <wx/string.h>
//*)

//(*IdInit(KeywordDialog)
const long KeywordDialog::ID_NAME_BOX = wxNewId();
const long KeywordDialog::ID_SORTNAME_BOX = wxNewId();
const long KeywordDialog::ID_ALIASES_BOX = wxNewId();
const long KeywordDialog::ID_HINTS_BOX = wxNewId();
const long KeywordDialog::ID_DESC_BOX = wxNewId();
const long KeywordDialog::ID_INSERT_SOURCE = wxNewId();
const long KeywordDialog::ID_NAME = wxNewId();
//*)

BEGIN_EVENT_TABLE(KeywordDialog,wxPanel)
	//(*EventTable(KeywordDialog)
	//*)
END_EVENT_TABLE()

KeywordDialog::KeywordDialog(wxWindow* parent, AppFrame* top_frame, const Keyword& keyword)
{
    this->top_frame = top_frame;
    this->keyword = keyword;

	//(*Initialize(KeywordDialog)
	wxStaticText* name_label;

	Create(parent, wxID_ANY, wxDefaultPosition, wxSize(401,238), wxTAB_TRAVERSAL, _T("wxID_ANY"));
	top_sizer = new wxBoxSizer(wxVERTICAL);
	fields_sizer = new wxFlexGridSizer(0, 2, 0, 0);
	fields_sizer->AddGrowableCol(1);
	name_label = new wxStaticText(this, wxID_ANY, _("&Keyword"), wxDefaultPosition, wxDefaultSize, 0, _T("wxID_ANY"));
	fields_sizer->Add(name_label, 0, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);
	name_box = new wxTextCtrl(this, ID_NAME_BOX, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator, _T("ID_NAME_BOX"));
	fields_sizer->Add(name_box, 1, wxALL|wxEXPAND|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);
	sortname_label = new wxStaticText(this, wxID_ANY, _("S&orting name"), wxDefaultPosition, wxDefaultSize, 0, _T("wxID_ANY"));
	fields_sizer->Add(sortname_label, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);
	sortname_box = new wxTextCtrl(this, ID_SORTNAME_BOX, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator, _T("ID_SORTNAME_BOX"));
	fields_sizer->Add(sortname_box, 1, wxALL|wxEXPAND|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);
	aliases_label = new wxStaticText(this, wxID_ANY, _("&Aliases"), wxDefaultPosition, wxDefaultSize, 0, _T("wxID_ANY"));
	fields_sizer->Add(aliases_label, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);
	aliases_box = new wxTextCtrl(this, ID_ALIASES_BOX, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE, wxDefaultValidator, _T("ID_ALIASES_BOX"));
	fields_sizer->Add(aliases_box, 1, wxALL|wxEXPAND|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);
	hints_label = new wxStaticText(this, wxID_ANY, _("&Hints"), wxDefaultPosition, wxDefaultSize, 0, _T("wxID_ANY"));
	fields_sizer->Add(hints_label, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);
	hints_box = new wxTextCtrl(this, ID_HINTS_BOX, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE, wxDefaultValidator, _T("ID_HINTS_BOX"));
	fields_sizer->Add(hints_box, 1, wxALL|wxEXPAND|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);
	desc_label = new wxStaticText(this, wxID_ANY, _("&Description"), wxDefaultPosition, wxDefaultSize, 0, _T("wxID_ANY"));
	fields_sizer->Add(desc_label, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);
	desc_box = new wxTextCtrl(this, ID_DESC_BOX, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE, wxDefaultValidator, _T("ID_DESC_BOX"));
	desc_box->SetFocus();
	fields_sizer->Add(desc_box, 1, wxALL|wxEXPAND|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);
	top_sizer->Add(fields_sizer, 1, wxALL|wxEXPAND|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);
	buttons_sizer = new wxBoxSizer(wxHORIZONTAL);
	insert_source_button = new wxButton(this, ID_INSERT_SOURCE, _("&Insert source"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator, _T("ID_INSERT_SOURCE"));
	buttons_sizer->Add(insert_source_button, 0, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);
	name_button = new wxButton(this, ID_NAME, _("&Person"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator, _T("ID_NAME"));
	buttons_sizer->Add(name_button, 0, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);
	buttons_sizer->Add(-1,-1,1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);
	save_button = new wxButton(this, wxID_SAVE, _("&Save"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator, _T("wxID_SAVE"));
	buttons_sizer->Add(save_button, 0, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);
	cancel_button = new wxButton(this, wxID_CANCEL, _("&Cancel"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator, _T("wxID_CANCEL"));
	buttons_sizer->Add(cancel_button, 0, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);
	top_sizer->Add(buttons_sizer, 0, wxALL|wxEXPAND|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);
	SetSizer(top_sizer);
	SetSizer(top_sizer);
	Layout();

	Connect(ID_NAME_BOX,wxEVT_COMMAND_TEXT_UPDATED,(wxObjectEventFunction)&KeywordDialog::OnNameChanged);
	Connect(ID_SORTNAME_BOX,wxEVT_COMMAND_TEXT_UPDATED,(wxObjectEventFunction)&KeywordDialog::OnSortnameChanged);
	Connect(ID_ALIASES_BOX,wxEVT_COMMAND_TEXT_UPDATED,(wxObjectEventFunction)&KeywordDialog::OnAliasesChanged);
	Connect(ID_HINTS_BOX,wxEVT_COMMAND_TEXT_UPDATED,(wxObjectEventFunction)&KeywordDialog::OnHintsChanged);
	Connect(ID_INSERT_SOURCE,wxEVT_COMMAND_BUTTON_CLICKED,(wxObjectEventFunction)&KeywordDialog::OnInsertSource);
	Connect(ID_NAME,wxEVT_COMMAND_BUTTON_CLICKED,(wxObjectEventFunction)&KeywordDialog::OnName);
	Connect(wxID_SAVE,wxEVT_COMMAND_BUTTON_CLICKED,(wxObjectEventFunction)&KeywordDialog::OnSave);
	Connect(wxID_CANCEL,wxEVT_COMMAND_BUTTON_CLICKED,(wxObjectEventFunction)&KeywordDialog::OnCancel);
	//*)

    fields_sizer->AddGrowableRow(2, 1);
    fields_sizer->AddGrowableRow(3, 1);
    fields_sizer->AddGrowableRow(4, 5);

    name_box->SetValue(keyword.name);
    sortname_box->SetValue(keyword.sortname);
    aliases_box->SetValue(keyword.aliases);
    hints_box->SetValue(keyword.hints);
    desc_box->SetValue(keyword.desc);

    // set up kbg handler to allow TAB switching in multiline wxTextCtrl on GTK
    // see: http://trac.wxwidgets.org/ticket/12999
    // also see the event handler
    #ifdef TARGET_LINUX
    aliases_box->Connect(wxEVT_CHAR, wxKeyEventHandler(KeywordDialog::OnChildKeyDown), 0, this);
    hints_box  ->Connect(wxEVT_CHAR, wxKeyEventHandler(KeywordDialog::OnChildKeyDown), 0, this);
    desc_box   ->Connect(wxEVT_CHAR, wxKeyEventHandler(KeywordDialog::OnChildKeyDown), 0, this);
    #endif // TARGET_LINUX

    name_box->SetFocus();
}

KeywordDialog::~KeywordDialog()
{
	//(*Destroy(KeywordDialog)
	//*)
}

bool KeywordDialog::isModified() const
{
    if (keyword.name != name_box->GetValue())
        return true;
    if (keyword.sortname != sortname_box->GetValue())
        return true;
    if (keyword.aliases != aliases_box->GetValue())
        return true;
    if (keyword.hints != hints_box->GetValue())
        return true;
    if (keyword.desc != desc_box->GetValue())
        return true;

    return false;
}

void KeywordDialog::OnCancel(wxCommandEvent& event)
{
    top_frame->CloseTab();
}

void KeywordDialog::OnSave(wxCommandEvent& event)
{
    // check for presence of unicode combining character. (we don't support 'em)
    // do this first, if combining char is root of problem, report it instead of refusing it as "incorrect format"
    #define CHECK_UNICODE(x) if(wxString_CheckCombining((x)->GetValue())) {(x)->SetFocus(); return; }
    CHECK_UNICODE(name_box);
    CHECK_UNICODE(sortname_box);
    CHECK_UNICODE(aliases_box);
    CHECK_UNICODE(hints_box);
    CHECK_UNICODE(desc_box);

    // check fields
    if (name_box->GetValue().IsEmpty()) {
        ErrMsg(_("Name can't be empty"));
        name_box->SetFocus();
        return;
    }
    if (!CheckKeyword(name_box->GetValue())) {
        name_box->SetFocus();
        return;
    }
    if (sortname_box->GetValue().IsEmpty()) {
        ErrMsg(_("SortName can't be empty"));
        sortname_box->SetFocus();
        return;
    }
    if (!CheckKeywordsString(aliases_box->GetValue())) {
        aliases_box->SetFocus();
        return;
    }
    if (!CheckKeywordsString(hints_box->GetValue(), "*")) {
        hints_box->SetFocus();
        return;
    }

    // disallow duplicate hint (causes trouble in keywords.cpp)
    vector<wxString> hints;
    wxASSERT(KeywordsFromString(hints_box->GetValue(), hints, "*"));
    for (unsigned x=0; x<hints.size(); x++)
        for (unsigned y=x+1; y<hints.size(); y++)
            if (hints[x] == hints[y]) {
                ErrMsg(_("Duplicate hint '%s'"), hints[x]);
                hints_box->SetFocus();
                return;
            }


    // if creating new keyword, check for duplicate name
    if (keyword.id==0 && keyword_list.ContainsName(name_box->GetValue())) {
        ErrMsg(_("Keyword with name '%s' already exists!"), name_box->GetValue());
        name_box->SetFocus();
        return;
    }

    // extra warning if changing name of keyword
    // TODO - support refactoring
    if (keyword.id!=0 && keyword.name != name_box->GetValue()) {
        wxString prompt = wxFormat(
                _("You are about to change name of keyword from '%s' to '%s'.\n\n"
                  "Notes referring to it won't be updated, they will still refer to old name.\n\n"
                  "Are you sure you want to continue?"),
                keyword.name, name_box->GetValue());
        if (!Confirm(prompt))
            return;
    }

    // load keyword from fields
    if (isModified()) {
        keyword.name        = name_box->GetValue();
        keyword.sortname    = sortname_box->GetValue();
        keyword.aliases     = aliases_box->GetValue();
        keyword.hints       = hints_box->GetValue();
        keyword.desc        = desc_box->GetValue();
        if (keyword.id == 0)
            keyword.Created();
        keyword.Modified();
        keyword.Used();
    }

    // tell app to save this keyword
    top_frame->SaveKeyword(keyword);

    // close this dialog
    top_frame->CloseTab();
}

void KeywordDialog::OnNameChanged(wxCommandEvent& event)
{
    if (CheckKeyword(name_box->GetValue()))
        name_box->SetBackgroundColour(wxNullColour);
    else
        name_box->SetBackgroundColour(*wxRED);
    name_box->Refresh();  // required on wxMSW
}

void KeywordDialog::OnAliasesChanged(wxCommandEvent& event)
{
    if (CheckKeywordsString(aliases_box->GetValue()))
        aliases_box->SetBackgroundColour(wxNullColour);
    else
        aliases_box->SetBackgroundColour(*wxRED);
    aliases_box->Refresh();
}

void KeywordDialog::OnHintsChanged(wxCommandEvent& event)
{
    if (CheckKeywordsString(hints_box->GetValue(), "*"))
        hints_box->SetBackgroundColour(wxNullColour);
    else
        hints_box->SetBackgroundColour(*wxRED);
    hints_box->Refresh();
}

void KeywordDialog::OnSortnameChanged(wxCommandEvent& event)
{
    if (!sortname_box->GetValue().IsEmpty())
        sortname_box->SetBackgroundColour(wxNullColour);
    else
        sortname_box->SetBackgroundColour(*wxRED);
    sortname_box->Refresh();
}

int KeywordDialog::getID() const
{
    return keyword.id;
}

void KeywordDialog::OnInsertSource(wxCommandEvent& event)
{
    // if source is not specified, offer user to set it now
    if (top_frame->source == wxT("")) {
        if (!InputString(&top_frame->source, _("Source:"), _("Set Source name")))
            return;
    }

    wxString source_formatted = wxT("(") + top_frame->source + wxT(", s. )");
    desc_box->WriteText(source_formatted);
    desc_box->SetInsertionPoint(desc_box->GetInsertionPoint()-1);
    desc_box->SetFocus();
}

void KeywordDialog::OnName(wxCommandEvent& event)
{
    wxString keywords, sortname, hints;
    NameDialog name_dialog(top_frame, &keywords, &sortname, &hints);
    if (name_dialog.ShowModal() == wxID_CANCEL)
        return;
    name_box->SetValue(keywords);
    sortname_box->SetValue(sortname);
    hints_box->SetValue(hints);
}

#ifdef TARGET_LINUX
/// Allow Tab traversal over GTK wxTextCtrl with just Tab, not Ctrl+Tab
/// See http://trac.wxwidgets.org/ticket/12999
void KeywordDialog::OnChildKeyDown(wxKeyEvent& event)
{
    dbg("KeywordDialog::OnChildKeyDown(), keycode=%u, ctrl=%u\n", event.GetKeyCode(), event.ControlDown());

    // if this is just Tab, navigate to next control
    if (event.GetKeyCode() == WXK_TAB && !event.ControlDown()) {

        // TODO: it seems kinda hard to force native tab traversal, or to find next tab traversal window
        // ourselves, so just hardcode it for now.
        // TODO: http://stackoverflow.com/questions/182739/is-it-possible-to-enumerate-the-wxframe-children-in-wxwidgets
        //   a very simple tab traversal simulation (enough for simple dialogs like this) should be possible this way
        wxWindow* cur_win = (wxWindow*)event.GetEventObject();
        if (cur_win == aliases_box) {
            if (event.ShiftDown())
                sortname_box->SetFocus();
            else
                hints_box->SetFocus();
        } else if (cur_win == hints_box) {
            if (event.ShiftDown())
                aliases_box->SetFocus();
            else
                desc_box->SetFocus();
        } else if (cur_win == desc_box) {
            if (event.ShiftDown())
                hints_box->SetFocus();
            else
                insert_source_button->SetFocus();
        }
        return;
    }

    // Since wx3.0 multiline wxTextCtrl steal CTRL+PageUp and CTRL+PageDown.
    // We use them here, so don't let him get it.
    if (event.ControlDown()) {
        if (event.GetKeyCode() == WXK_PAGEDOWN) {
            top_frame->NextTab();
            return;
        }
        if (event.GetKeyCode() == WXK_PAGEUP) {
            top_frame->PrevTab();
            return;
        }
    }

    event.Skip();
}
#endif
